(function (angular) {
    'use strict';
    angular.module("fs.validator").directive("fsValidator", fsValidatorDirective);

    fsValidatorDirective.$inject = ['$q', '$injector', '$parse', '$timeout', '$translate', 'loggerFactory', 'formatService', 'validateUtil'];
    function fsValidatorDirective($q, $injector, $parse, $timeout, $translate, loggerFactory, formatService, validateUtil) {
        return {
            restrict: 'A',
            require: ['ngModel', '^^?form', '^^fsGrid', '^?uiGrid'],
            controller: function () { },
            link: function ($scope, $element, $attrs, ctrls) {
                var log = loggerFactory.getLogger(), rules, ruleInfos, validator, fsGridCtrl = ctrls[2], uiGridCtrl = ctrls[3], newRuleInfos = [];
                var ruleName, watcher, watchers = [];
                $scope.$on('$destroy', function () {
                    while (watchers.length) {
                        (watchers.shift() || angular.noop)();
                    }
                });
                if (!$scope.fsGridConfig.rules) {
                    return;
                }
                $scope.fsGridConfig.rules = rules = angular.extend({}, $scope.fsGridConfig.rules);
                ruleInfos = rules.ruleInfos || [];
                if ($attrs.altInputFormats) {
                    buildInputDateCheck(ruleInfos);
                }
                //add this for support the number-range percentage-range, money-range compare validator.
                if ($scope.fsGridConfig.type && new RegExp("([a-zA-z0-9]-range)").test($scope.fsGridConfig.type)) {
                    buildCompareRange(ruleInfos);
                } else if (ruleInfos.length == 0) {
                    return;
                }

                angular.forEach(ruleInfos, function (rule) {
                    if (!rule.fieldCtrl || rule.fieldCtrl.$name == ctrls[0].$name) {
                        rule.fieldCtrl = ctrls[0];
                    } else if (!rule.fieldCtrl2 || rule.fieldCtrl2.$name == ctrls[0].$name) {
                        rule.fieldCtrl2 = ctrls[0];
                    }
                    ruleName = angular.lowercase(rule.ruleName);
                    rule.operatedField = rules.field;
                    rule.fieldLabel = rules.fieldLabel;
                    rule.appScope = getGridAppScope();
                    rule.rowDatas = getAllRowDatas();
                    if (fsGridCtrl != null) {
                        rule.uiGridRow = fsGridCtrl.getUiGridRow();
                    }
                    rule.$scope = $scope;
                    rule.fsGridCtrl = ctrls[2];
                    if ("conditiondependent" == ruleName) {
                        watcher = $scope.$watch('ngModel.' + rule.operatedField, function (modelValue, oldValue, scope) {
                            $timeout(function () {
                                validateUtil.dependCheckByConditionChanged(rule, modelValue);
                            });
                        });
                        watchers.push(watcher);
                    } else if ("dependent" == ruleName) {
                        ctrls[0].$asyncValidators[rule.ruleName] = function (modelValue, viewValue) {
                            rule.ngModel = getGridEntity();
                            return validate(rule, modelValue, viewValue);
                        };
                        validateUtil.registerDependCheck(rule);
                    } else if ("uniquecheck" == ruleName) {
                        validateUtil.registerUniqueCheckCheck(rule);
                        watcher = $scope.$watch('ngModel.' + rule.operatedField, function (modelValue, oldValue, scope) {
                            $timeout(function () {
                                var validator, compareRules = validateUtil.getUniqueCheckRules(rule), validateResult;
                                angular.forEach(compareRules, function (r) {
                                    r.ngModel = getGridEntity();
                                    validator = $injector.get(angular.lowercase(r.ruleName));
                                    validator.validate(modelValue, r.fieldCtrl.$viewValue, r).then(function resolve() {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        if (rule.operatedField == r.operatedField) {
                                            fsGridCtrl.removeValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, true);
                                    }, function reject(error) {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = true;
                                        validateResult.messages = error.error;
                                        if (rule.operatedField == r.operatedField) {
                                            fsGridCtrl.saveValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, false);
                                    });
                                });
                            });
                        });
                        watchers.push(watcher);
                    } else if ("equalto" == ruleName || "numbercompare" == ruleName || "datecompare" == ruleName || "datebetweencompare" == ruleName || "atleastonerequired" == ruleName) {
                        //fixed bug for the validator in the grid table
                        if (rule.uiGridRow) rule = angular.extend({}, rule);
                        validateUtil.registerCompareCheck(rule);
                        watcher = $scope.$watch('ngModel.' + rule.operatedField, function (modelValue, oldValue, scope) {
                            $timeout(function () {
                                var validator, compareRules = validateUtil.getCompareRules(rule), validateResult;
                                angular.forEach(compareRules, function (r) {
                                    r.ngModel = getGridEntity();
                                    validator = $injector.get(angular.lowercase(r.ruleName));
                                    validator.validate(modelValue, r.fieldCtrl.$viewValue, r).then(function resolve() {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        // fixed bug for atleastonerequired validator.
                                        if ("atleastonerequired" == angular.lowercase(r.ruleName)) {
                                            r.fsGridCtrl.removeValidateMessage(validateResult, fieldFormCtrl);
                                        } else if (rule.operatedField == r.operatedField) {
                                            r.fsGridCtrl.removeValidateMessage(validateResult, fieldFormCtrl);
                                        }
                                        if (isSpecialModelType(modelValue)) {
                                            if (!isEmpty(modelValue.from) || !isEmpty(modelValue.to)) {
                                                r.fieldCtrl.$setDirty(r.ruleName, true);
                                            }
                                        } else if (!isEmpty(modelValue)) {
                                            r.fieldCtrl.$setDirty(r.ruleName, true);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, true);
                                    }, function reject(error) {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = true;
                                        validateResult.messages = error.error;
                                        if ("atleastonerequired" == angular.lowercase(r.ruleName)) {
                                            r.fsGridCtrl.saveValidateMessage(validateResult, fieldFormCtrl);
                                        } else if (rule.operatedField == r.operatedField) {
                                            r.fsGridCtrl.saveValidateMessage(validateResult, fieldFormCtrl);
                                        }
                                        if (isSpecialModelType(modelValue)) {
                                            if (!isEmpty(modelValue.from) || !isEmpty(modelValue.to)) {
                                                r.fieldCtrl.$setDirty(r.ruleName, true);
                                            }
                                        } else if (!isEmpty(modelValue)) {
                                            r.fieldCtrl.$setDirty(r.ruleName, true);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, false);
                                    });
                                });
                            });
                        }, true);
                        watchers.push(watcher);
                    } else if ("rowcompare" == ruleName) {
                        validateUtil.registerRowCompareCheck(rule);
                        $scope.$on('$destroy', function () {
                            $timeout(function () {
                                var validator, compareRules = validateUtil.getRowCompareRules(rule), validateResult;
                                angular.forEach(compareRules, function (r) {
                                    r.ngModel = getGridEntity();
                                    validator = $injector.get(angular.lowercase(r.ruleName));
                                    validator.validate('', r.fieldCtrl.$viewValue, r).then(function resolve() {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        if (rule.operatedField == r.operatedField) {
                                            r.fsGridCtrl.removeValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, true);
                                    }, function reject(error) {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = true;
                                        validateResult.messages = error.error;
                                        if (rule.operatedField == r.operatedField) {
                                            r.fsGridCtrl.saveValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, false);
                                    });
                                });
                            });
                        });
                        watcher = $scope.$watch('ngModel.' + rule.operatedField, function (modelValue, oldValue, scope) {
                            $timeout(function () {
                                var validator, compareRules = validateUtil.getRowCompareRules(rule), validateResult;
                                angular.forEach(compareRules, function (r) {
                                    r.ngModel = getGridEntity();
                                    validator = $injector.get(angular.lowercase(r.ruleName));
                                    validator.validate(modelValue, r.fieldCtrl.$viewValue, r).then(function resolve() {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        if (rule.operatedField == r.operatedField) {
                                            fsGridCtrl.removeValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, true);
                                    }, function reject(error) {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = true;
                                        validateResult.messages = error.error;
                                        if (rule.operatedField == r.operatedField) {
                                            r.fsGridCtrl.saveValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, false);
                                    });
                                });
                            });
                        });
                        watchers.push(watcher);
                    } else if ("rowuniquecheck" == ruleName) {
                        validateUtil.registerUniqueCheckCheck(rule);
                        $scope.$on('$destroy', function () {
                            $timeout(function () {
                                var validator, compareRules = validateUtil.getUniqueCheckRules(rule), validateResult;
                                angular.forEach(compareRules, function (r) {
                                    r.ngModel = getGridEntity();
                                    validator = $injector.get(angular.lowercase(r.ruleName));
                                    validator.validate('', r.fieldCtrl.$viewValue, r).then(function resolve() {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        if (rule.operatedField == r.operatedField) {
                                            r.fsGridCtrl.removeValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, true);
                                    }, function reject(error) {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = true;
                                        validateResult.messages = error.error;
                                        if (rule.operatedField == r.operatedField) {
                                            r.fsGridCtrl.saveValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, false);
                                    });
                                });
                            });
                        });
                        watcher = $scope.$watch('ngModel.' + rule.operatedField, function (modelValue, oldValue, scope) {
                            $timeout(function () {
                                var validator, compareRules = validateUtil.getUniqueCheckRules(rule), validateResult;
                                angular.forEach(compareRules, function (r) {
                                    r.ngModel = getGridEntity();
                                    validator = $injector.get(angular.lowercase(r.ruleName));
                                    validator.validate(modelValue, r.fieldCtrl.$viewValue, r).then(function resolve() {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        if (rule.operatedField == r.operatedField) {
                                            r.fsGridCtrl.removeValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, true);
                                    }, function reject(error) {
                                        validateResult = {
                                            'field': r.operatedField,
                                            'rule': r.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = true;
                                        validateResult.messages = error.error;
                                        if (rule.operatedField == r.operatedField) {
                                            r.fsGridCtrl.saveValidateMessage(validateResult);
                                        }
                                        r.fieldCtrl.$setValidity(r.ruleName, false);
                                    });
                                });
                            });
                        });
                        watchers.push(watcher);
                    } else if ("table" == $scope.fsGridConfig.type || "innerTable" == $scope.fsGridConfig.type) {
                        var watchProperty, fieldCtrl;
                        if ($scope.fsGridConfig.tableConfig.allowSelectedValid) {
                            watchProperty = 'ngModel.' + rule.operatedField + 'Selected';
                            watcher = $scope.$watchCollection(watchProperty, function (modelValue, oldValue, scope) {
                                $timeout(function () {
                                    var validateResult, isValid = true, compareResult, validator;
                                    validator = $injector.get("required");
                                    fieldCtrl = rule.fieldCtrl2;
                                    fieldCtrl.$setDirty(rule.ruleName, true);
                                    validator.validate(modelValue, fieldCtrl.$viewValue, rule).then(function resolve() {
                                        validateResult = {
                                            'field': rule.operatedField,
                                            'rule': rule.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        rule.fsGridCtrl.removeValidateMessage(validateResult);
                                        fieldCtrl.$setValidity(rule.ruleName, true);
                                    }, function reject(error) {
                                        validateResult = {
                                            'field': rule.operatedField,
                                            'rule': rule.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = true;
                                        validateResult.messages = error.error;
                                        rule.fsGridCtrl.saveValidateMessage(validateResult);
                                        fieldCtrl.$setValidity(rule.ruleName, false);
                                    });
                                });
                            });
                        } else {
                            watchProperty = 'ngModel.' + rule.operatedField;
                            watcher = $scope.$watchCollection(watchProperty, function (modelValue, oldValue, scope) {
                                $timeout(function () {
                                    var validateResult, isValid = true, compareResult, validator;
                                    validator = $injector.get("required");
                                    fieldCtrl = rule.fieldCtrl;
                                    fieldCtrl.$setDirty(rule.ruleName, true);
                                    validator.validate(modelValue, fieldCtrl.$viewValue, rule).then(function resolve() {
                                        validateResult = {
                                            'field': rule.operatedField,
                                            'rule': rule.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        rule.fsGridCtrl.removeValidateMessage(validateResult);
                                        fieldCtrl.$setValidity(rule.ruleName, true);
                                    }, function reject(error) {
                                        validateResult = {
                                            'field': rule.operatedField,
                                            'rule': rule.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = true;
                                        validateResult.messages = error.error;
                                        rule.fsGridCtrl.saveValidateMessage(validateResult);
                                        fieldCtrl.$setValidity(rule.ruleName, false);
                                    });
                                });
                            });
                        }
                        watchers.push(watcher);
                    } else {
                        if ($scope.fsGridConfig.type && !new RegExp("([a-zA-z0-9]-range)").test($scope.fsGridConfig.type)) {
                            ctrls[0].$asyncValidators[rule.ruleName] = function (modelValue, viewValue) {
                                rule.ngModel = getGridEntity();
                                return validate(rule, modelValue, viewValue);
                            };
                        }
                    }
                    // $scope.fsGridConfig.type like "date-range", "datetime-range", "number-range", "money-range", "pencentage-range"
                    if ($scope.fsGridConfig.type && new RegExp("([a-zA-z0-9]-range)").test($scope.fsGridConfig.type)) {
                        rule.specialModelTypeFlag = true;
                        if ("date-range" == $scope.fsGridConfig.type) {
                            rule.format = "date";
                        } else if ("datetime-range" == $scope.fsGridConfig.type) {
                            rule.format = "timestamp";
                        }
                        watcher = $scope.$watch('ngModel.' + rule.operatedField, function (modelValue, oldValue, scope) {
                            $timeout(function () {
                                var validateResult, isValid = true, fromValue, toValue, compareResult, validator, fieldCtrlFrom, fieldCtrlTo;
                                // common validate.
                                if (!("atleastonerequired" == angular.lowercase(rule.ruleName) || "comparerange" == angular.lowercase(rule.ruleName))) {
                                    validator = $injector.get(angular.lowercase(rule.ruleName));
                                    fieldCtrlFrom = rule.fieldCtrl;
                                    validator.validate(modelValue, fieldCtrlFrom.$viewValue, rule).then(function resolve() {
                                        validateResult = {
                                            'field': fieldCtrlFrom.$name,
                                            'rule': rule.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        rule.fsGridCtrl.removeValidateMessage(validateResult);
                                        fieldCtrlFrom.$setValidity(rule.ruleName, true);
                                    }, function reject(error) {
                                        validateResult = {
                                            'field': fieldCtrlFrom.$name,
                                            'rule': rule.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = true;
                                        validateResult.messages = error.error;
                                        rule.fsGridCtrl.saveValidateMessage(validateResult);
                                        fieldCtrlFrom.$setValidity(rule.ruleName, false);
                                    });

                                    fieldCtrlTo = rule.fieldCtrl2;
                                    if (fieldCtrlTo) {
                                        validator.validate(modelValue, fieldCtrlTo.$viewValue, rule).then(function resolve() {
                                            validateResult = {
                                                'field': fieldCtrlTo.$name,
                                                'rule': rule.ruleName,
                                                'messages': null
                                            };
                                            validateResult.invalid = false;
                                            rule.fsGridCtrl.removeValidateMessage(validateResult);
                                            fieldCtrlTo.$setValidity(rule.ruleName, true);
                                        }, function reject(error) {
                                            validateResult = {
                                                'field': fieldCtrlTo.$name,
                                                'rule': rule.ruleName,
                                                'messages': null
                                            };
                                            validateResult.invalid = true;
                                            validateResult.messages = error.error;
                                            rule.fsGridCtrl.saveValidateMessage(validateResult);
                                            fieldCtrlTo.$setValidity(rule.ruleName, false);
                                        });
                                    }
                                }
                                if ("comparerange" == angular.lowercase(rule.ruleName)) {
                                    // add fromDate <= toDate Rule
                                    if (modelValue && modelValue.from && modelValue.to) {
                                        fromValue = modelValue.from;
                                        toValue = modelValue.to;
                                        if ("date-range" == $scope.fsGridConfig.type) {
                                            fromValue = clearTime(angular.copy(modelValue.from));
                                            toValue = clearTime(angular.copy(modelValue.to));
                                        }
                                        if ("date-range" == $scope.fsGridConfig.type || "datetime-range" == $scope.fsGridConfig.type) {
                                            compareResult = compareDateTime(fromValue, toValue);
                                            isValid = eval(compareResult + "<=0");
                                        } else {
                                            isValid = arithService.lte(fromValue, toValue);
                                        }
                                    }
                                    if (isValid) {
                                        validateResult = {
                                            'field': rule.operatedField,
                                            'rule': rule.ruleName,
                                            'messages': null
                                        };
                                        validateResult.invalid = false;
                                        rule.fsGridCtrl.removeValidateMessage(validateResult);
                                        rule.fieldCtrl.$setValidity(rule.ruleName, true);

                                    } else {
                                        var deferred = $q.defer()
                                        buildErrorMsg(rule, deferred, $translate).then(function (error) {
                                        }, function (error) {
                                            validateResult = {
                                                'field': rule.operatedField,
                                                'rule': rule.ruleName,
                                                'messages': null
                                            };
                                            validateResult.invalid = true;
                                            validateResult.messages = error.error;
                                            rule.fsGridCtrl.saveValidateMessage(validateResult);
                                            rule.fieldCtrl.$setValidity(rule.ruleName, false);
                                        });
                                    }
                                }
                            });
                        }, true);
                        watchers.push(watcher);
                    }
                });
                function getGridEntity() {
                    return fsGridCtrl.getGridEntity();
                }
                function getGridAppScope() {
                    var appScope = fsGridCtrl.getGridAppScope();
                    return appScope;
                }
                function getAllRowDatas() {
                    var rowDatas = [];
                    if (uiGridCtrl) {
                        rowDatas = $parse(uiGridCtrl.grid.options.data)(getGridAppScope());
                    }
                    return rowDatas;
                }
                function buildInputDateCheck(ruleInfos) {
                    var rules, dateInputFormats = $scope.$eval($attrs.altInputFormats);
                    if (dateInputFormats.length > 0 && dateInputFormats[0]) {
                        rules = {
                            'field': $scope.fsGridConfig.model,
                            'fieldLabel': $scope.fsGridConfig.label || $scope.fsGridConfig.displayName,
                            'ruleInfos': [{
                                'errorArgs': [$scope.fsGridConfig.label || $scope.fsGridConfig.displayName, dateInputFormats[0]],
                                'errorMsg': 'validation.dateformatcheck',
                                'ruleName': 'dateformatcheck',
                                'dateInputFormat': dateInputFormats[0],
                                'pattern': '^' + dateInputFormats[0].replace(/yyyy/, '[0-9]{4}').replace(/MM/, '(?:(?:0[0-9])|(?:1[0-2]))').replace('dd', '[0-3][0-9]').replace('HH', '[0-2][0-9]').replace('mm', '[0-6][0-9]').replace('ss', '[0-6][0-9]') + '$'
                            },
                            {
                                'errorArgs': [$scope.fsGridConfig.label || $scope.fsGridConfig.displayName],
                                'errorMsg': 'validation.holidaycheck',
                                'ruleName': 'holidaycheck'
                            }
                            ]
                        };

                        ruleInfos.push(rules.ruleInfos[0]);
                        ruleInfos.push(rules.ruleInfos[1]);
                        ctrls[0].$parsers[0] = function (viewValue) {
                            try {
                                return formatService.parseDate(viewValue, dateInputFormats[0]);
                            } catch (e) {
                                log.debug('can not parse the date value: ' + viewValue + ' by format ' + dateInputFormats[0]);
                                return null;
                            }
                        };
                        ctrls[0].$validators.date = function (modelValue, viewValue) {
                            return true;
                        };
                    }
                }
                function buildCompareRange(ruleInfos) {
                    var errorArgs = [$scope.fsGridConfig.label, "label.global.from", "label.global.to"], errorMsg;
                    if ("date-range" == $scope.fsGridConfig.type || "datetime-range" == $scope.fsGridConfig.type) {
                        errorMsg = "validation.datecompare.daterange.earlier";
                    } else {
                        errorMsg = "validation.numbercompare.numberrange.less";
                    }
                    var rules = {
                        'field': $scope.fsGridConfig.model,
                        'fieldLabel': $scope.fsGridConfig.label || $scope.fsGridConfig.displayName,
                        'ruleInfos': [
                            {
                                'errorArgs': errorArgs,
                                'errorMsg': errorMsg,
                                'ruleName': 'compareRange'
                            }
                        ]
                    }
                    ruleInfos.push(rules.ruleInfos[0]);
                }
                function validate(rule, modelValue, viewValue) {
                    var defer = $q.defer(), validateResult = {
                        'field': rule.operatedField,
                        'rule': rule.ruleName,
                        'messages': null
                    };
                    try {
                        validator = $injector.get(angular.lowercase(rule.ruleName));
                        validator.validate(modelValue, viewValue, rule).then(function resolve() {
                            defer.resolve(true);
                            validateResult.invalid = false;
                            rule.fsGridCtrl.removeValidateMessage(validateResult);
                        }, function reject(error) {
                            defer.reject(error);
                            validateResult.messages = error.error;
                            validateResult.invalid = true;
                            rule.fsGridCtrl.saveValidateMessage(validateResult);
                        });
                    } catch (e) {
                        log.debug('validate exception: ' + rule.ruleName);
                        log.debug(e);
                        defer.resolve(true);
                    }
                    return defer.promise;
                }
            }
        }
    }

    function isEmpty(value) {
        return (value === null || angular.isUndefined(value) || (angular.isString(value) ? value.trim() == '' : false || (angular.isArray(value) && value.length == 0)));
    }

    function isSpecialModelType(value) {
        if (angular.isObject(value) && !angular.isDate(value) && !angular.isArray(value)) {
            return true;
        } else {
            return false;
        }
    }

    function buildErrorMsg(rule, deferred, $translate) {
        var paramObj = {}, errorArgs = rule.errorArgs, errorMsg = rule.errorMsg;
        if (!errorArgs || !errorMsg) {
            deferred.reject({
                error: 'validator errorArgs or errorMsg is not exist in validator json file'
            });
        }
        $translate(errorArgs).then(function (objs) {
            if ("uniquecheck" == angular.lowercase(rule.ruleName) || "lookupcheck" == angular.lowercase(rule.ruleName)) {
                angular.forEach(errorArgs, function (key, index) {
                    if (errorArgs.length == 2) {
                        paramObj['p0'] = objs[errorArgs[0]];
                        paramObj['p1'] = rule.operatedFieldValue;
                        paramObj['p2'] = objs[errorArgs[1]];
                    }
                    if (errorArgs.length == 1) {
                        paramObj['p0'] = objs[errorArgs[0]];
                        paramObj['p1'] = rule.operatedFieldValue;
                    }
                });
            } else if ("dependent" == angular.lowercase(rule.ruleName) && rule.conditionPattern) {
                paramObj['p0'] = objs[errorArgs[0]];
                paramObj['p1'] = rule.conditionPattern.split(";")[rule.conditionFieldValueIndex];
                if (rule.validateRequiredFlag) {
                    errorMsg = "validation.required";
                } else {
                    errorMsg = errorMsg.split(";")[rule.conditionFieldValueIndex];
                }
            } else if ("rowcompare" == angular.lowercase(rule.ruleName)) {
                paramObj['p0'] = objs[errorArgs[0]];
                paramObj['p1'] = rule.rowNum;
            } else {
                angular.forEach(errorArgs, function (key, index) {
                    paramObj['p' + index] = objs[errorArgs[index]];
                });
            }

            $translate(errorMsg, paramObj).then(function (errorMsg) {
                deferred.reject({
                    error: errorMsg
                });
            }, function (msg) {
                deferred.reject({
                    error: msg
                });
            });
        }, function (msg) {
            deferred.reject({
                error: msg
            });
        });
    }
    function clearTime(date) {
        if (angular.isDate(date)) {
            date.setHours(0);
            date.setMinutes(0);
            date.setSeconds(0);
            date.setMilliseconds(0);
        }
        return date;
    }
    function compareDateTime(date1, date2) {
        if (!angular.isDate(date1) || !angular.isDate(date2)) {
            return 0;
        }
        var time1 = date1.getTime();
        var time2 = date2.getTime();
        if (time2 > time1) {
            return -1;
        } else if (time2 == time1) {
            return 0;
        } else {
            return 1;
        }
    }
})(angular);